#include "minesweeper.hpp"
#include "../score/score.hpp"

#include <iostream>

MineSweeper::MineSweeper() {
	
}

MineSweeper::~MineSweeper() {
	
}

void MineSweeper::init(Engine *engine) {
	for(std::map<int, Player>::iterator iPlayer = engine->players.begin(); iPlayer != engine->players.end(); iPlayer++) {
		int index = (*iPlayer).first;
		
		sf::Sprite target;
		target.SetImage(engine->getImage("data/minesweeper/target.png"));
		target.SetColor((*iPlayer).second.color);
		int x, y;
		do {
			x = sf::Randomizer::Random(0, 24), y = sf::Randomizer::Random(0, 15);
			target.SetPosition(32.f * x + 16.f, 88.f + 32.f * y + 16.f);
			target.SetCenter(18.f, 18.f);
		} while(!tileFree(x, y));
		
		sweepers[index].x = x;
		sweepers[index].y = y;
		sweepers[index].target = target;
	}
	
	for(int i = 0; i < 400; i++) {
		tiles[i] = Water;
		flags[i] = -1;
	}
	
    for(int j = 0; j < 400 * 0.15f; j++) {
        int k = sf::Randomizer::Random(0, 399);

		if(tiles[k] != HiddenMine) tiles[k] = HiddenMine;
		else j--;
    }
    
    clock.Reset();
    //lastTime = 0.f;
}

void MineSweeper::cleanup() {
	
}

void MineSweeper::pause() {
	
}

void MineSweeper::resume() {
	clock.Reset();
}

void MineSweeper::processEvent(Engine *engine, PlayerEvent& event) {
	switch(event.type) {
		case PlayerEvent::Quit:
		{
			if(event.state)
				engine->changeState(Score::getSingleton());
			break;
		}
		case PlayerEvent::Action1:
		{
			if(event.state) {
				if((sweepers[event.player].target.GetPosition().x != sweepers[event.player].x * 32.f + 16.f)
				|| (sweepers[event.player].target.GetPosition().y != 88.f + sweepers[event.player].y * 32.f + 16.f)) break;
				
				switch(tiles[sweepers[event.player].y * 25 + sweepers[event.player].x]) {
					case HiddenMine:
						bang(engine, event.player);
						break;
					case Water:
						recurse(sweepers[event.player].x, sweepers[event.player].y);
						break;
					default:
						break;
				}

				sweepers[event.player].target.SetScale(1.2f, 1.2f);
			}
			break;
		}
		case PlayerEvent::Action2:
		{
			if(event.state) {
				if((sweepers[event.player].target.GetPosition().x != sweepers[event.player].x * 32.f + 16.f)
				|| (sweepers[event.player].target.GetPosition().y != 88.f + sweepers[event.player].y * 32.f + 16.f)) break;
				
				flag(engine, event.player);
				sweepers[event.player].target.SetScale(1.2f, 1.2f);
			}
			break;
		}
		default:
			break;
	}
}

void MineSweeper::update(Engine *engine) {
	float time = clock.GetElapsedTime();
	clock.Reset();
	
	for(std::list<ScoreString>::iterator iScoreString = scoreStrings.begin(); iScoreString != scoreStrings.end(); iScoreString++) {
		(*iScoreString).time -= time;
		if((*iScoreString).time <= 0.f) {
			iScoreString = scoreStrings.erase(iScoreString);
			if(iScoreString == scoreStrings.end()) break;
			continue;
		}
		
		(*iScoreString).string.Move(sf::Vector2f(0.f, -time * 32.f));
		(*iScoreString).string.SetColor(sf::Color(255, 255, 255, int(((*iScoreString).time) * 255)));
	}
		
	for(std::map<int, Sweeper>::iterator iSweeper = sweepers.begin(); iSweeper != sweepers.end(); iSweeper++) {
		int index = (*iSweeper).first;
		Sweeper& sweeper = (*iSweeper).second;
		
		if((sweeper.target.GetPosition().x == sweeper.x * 32.f + 16.f)
		&& (sweeper.target.GetPosition().y == 88.f + sweeper.y * 32.f + 16.f)) {
			if((engine->players[index].actions[PlayerEvent::MoveLeft].state)
			&& (!engine->players[index].actions[PlayerEvent::MoveRight].state)) {
				if(tileFree(sweeper.x - 1, sweeper.y)) sweeper.x--;
			}
			else if((engine->players[index].actions[PlayerEvent::MoveRight].state)
			&& (!engine->players[index].actions[PlayerEvent::MoveLeft].state)) {
				if(tileFree(sweeper.x + 1, sweeper.y)) sweeper.x++;
			}
			
			if((engine->players[index].actions[PlayerEvent::MoveUp].state)
			&& (!engine->players[index].actions[PlayerEvent::MoveDown].state)) {
				if(tileFree(sweeper.x, sweeper.y - 1)) sweeper.y--;
			}
			else if((engine->players[index].actions[PlayerEvent::MoveDown].state)
			&& (!engine->players[index].actions[PlayerEvent::MoveUp].state)) {
				if(tileFree(sweeper.x, sweeper.y + 1)) sweeper.y++;
			}
		}
		
		if(sweeper.target.GetScale().x > 1.0f) {
			float scale = sweeper.target.GetScale().x - time;
			if(scale < 1.0f) scale = 1.0f;
			sweeper.target.SetScale(scale, scale);
		}
		
		if(sweeper.target.GetPosition().x < sweeper.x * 32.f + 16.f) {
			//sweeper.target.Move((sweeper.x * 32.f - sweeper.target.GetPosition().x) * time * 20.f, 0.f);
			sweeper.target.Move(300.f * time, 0.f);
			if(sweeper.target.GetPosition().x > sweeper.x * 32.f + 16.f) sweeper.target.SetX(sweeper.x * 32.f + 16.f);
		}
		
		if(sweeper.target.GetPosition().x > sweeper.x * 32.f + 16.f) {
			//sweeper.target.Move((sweeper.x * 32.f - sweeper.target.GetPosition().x) * time * 20.f, 0.f);
			sweeper.target.Move(-300.f * time, 0.f);
			if(sweeper.target.GetPosition().x < sweeper.x * 32.f + 16.f) sweeper.target.SetX(sweeper.x * 32.f + 16.f);
		}
		
		if(sweeper.target.GetPosition().y < 88.f + sweeper.y * 32.f + 16.f) {
			//sweeper.target.Move(0.f, (sweeper.y * 32.f + 88.f - sweeper.target.GetPosition().y) * time * 20.f);
			sweeper.target.Move(0.f, 300.f * time);
			if(sweeper.target.GetPosition().y > sweeper.y * 32.f + 88.f + 16.f) sweeper.target.SetY(88.f + sweeper.y * 32.f + 16.f);
		}
		
		if(sweeper.target.GetPosition().y > 88.f + sweeper.y * 32.f + 16.f) {
			//sweeper.target.Move(0.f, (sweeper.y * 32.f + 88.f - sweeper.target.GetPosition().y) * time * 20.f);
			sweeper.target.Move(0.f, -300.f * time);
			if(sweeper.target.GetPosition().y < sweeper.y * 32.f + 88.f + 16.f) sweeper.target.SetY(88.f + sweeper.y * 32.f + 16.f);
		}
	}
}

void MineSweeper::draw(Engine *engine) {
	for(int i = 0; i < 400; i++) {
		tileSprite.SetPosition(i % 25 * 32.f, 88.f + i / 25 * 32.f);
		
		switch(tiles[i]) {
			case Water:
			case HiddenMine:
				tileSprite.SetImage(engine->getImage("data/minesweeper/water.png"));
				break;
			case Sand:
				tileSprite.SetImage(engine->getImage("data/minesweeper/sand.png"));
				break;
			case One:
				tileSprite.SetImage(engine->getImage("data/minesweeper/1.png"));
				break;
			case Two:
				tileSprite.SetImage(engine->getImage("data/minesweeper/2.png"));
				break;
			case Three:
				tileSprite.SetImage(engine->getImage("data/minesweeper/3.png"));
				break;
			case Four:
				tileSprite.SetImage(engine->getImage("data/minesweeper/4.png"));
				break;
			case Five:
				tileSprite.SetImage(engine->getImage("data/minesweeper/5.png"));
				break;
			case Six:
				tileSprite.SetImage(engine->getImage("data/minesweeper/6.png"));
				break;
			case Seven:
				tileSprite.SetImage(engine->getImage("data/minesweeper/7.png"));
				break;
			case Eight:
				tileSprite.SetImage(engine->getImage("data/minesweeper/8.png"));
				break;
			case ExplodedMine:
				tileSprite.SetImage(engine->getImage("data/minesweeper/exploded.png"));
				break;
			default:
				break;
		}
		
		engine->window.Draw(tileSprite);
		
		if(flags[i] > -1) {
			tileSprite.SetImage(engine->getImage("data/minesweeper/flag.png"));
			tileSprite.SetColor(engine->players[flags[i]].color);
			engine->window.Draw(tileSprite);
			tileSprite.SetColor(sf::Color(255, 255, 255, 255));
		}
	}
	
	for(std::map<int, Sweeper>::iterator iSweeper = sweepers.begin(); iSweeper != sweepers.end(); iSweeper++)
		engine->window.Draw((*iSweeper).second.target);
	
	for(std::list<ScoreString>::iterator iScoreString = scoreStrings.begin(); iScoreString != scoreStrings.end(); iScoreString++)
		engine->window.Draw((*iScoreString).string);

}

bool MineSweeper::tileFree(int x, int y) {
	if((x < 0) || (y < 0) || (x > 24) || (y > 15)) return false;
	
	for(std::map<int, Sweeper>::iterator iSweeper = sweepers.begin(); iSweeper != sweepers.end(); iSweeper++) {
		if(((*iSweeper).second.x == x)
		&& ((*iSweeper).second.y == y)) return false;
	}
	
	return true;
}

bool MineSweeper::hasMine(int x, int y) {
	if((x < 0) || (y < 0) || (x > 24) || (y > 15)) return false;
	if((tiles[y * 25 + x] == HiddenMine) || (tiles[y * 25 + x] == ExplodedMine)) return true;
	return false;
}

bool MineSweeper::hasFlag(int x, int y) {
	if((x < 0) || (y < 0) || (x > 24) || (y > 15)) return false;
	if(flags[y * 25 + x] != -1) return true;
	return false;
}

int MineSweeper::countMines(int x, int y) {
	if((x < 0) || (y < 0) || (x > 24) || (y > 15)) return 0;
	int count = 0;
	
	if(hasMine(x - 1, y - 1)) count++;
	if(hasMine(x, y - 1)) count++;
	if(hasMine(x + 1, y - 1)) count++;
	
	if(hasMine(x - 1, y)) count++;
	if(hasMine(x + 1, y)) count++;

	if(hasMine(x - 1, y + 1)) count++;
	if(hasMine(x, y + 1)) count++;
	if(hasMine(x + 1, y + 1)) count++;
	
	return count;
}

void MineSweeper::recurse(int x, int y) {
	if((x < 0) || (y < 0) || (x > 24) || (y > 15)) return;
	if((tiles[y * 25 + x] != Water) || hasFlag(x, y)) return;
	
	int count = countMines(x, y);
	tiles[y * 25 + x] = Tile(count);
	flags[y * 25 + x] = -1;
	
	if(count == 0) {
		recurse(x, y - 1);
		recurse(x - 1, y);
		recurse(x + 1, y);
		recurse(x, y + 1);
		
		int count = countMines(x - 1, y - 1);
		if((count > 0) && !hasFlag(x - 1, y - 1)) tiles[(y - 1) * 25 + (x - 1)] = Tile(count);
		count = countMines(x + 1, y - 1);
		if((count > 0) && !hasFlag(x + 1, y - 1)) tiles[(y - 1) * 25 + (x + 1)] = Tile(count);
		count = countMines(x - 1, y + 1);
		if((count > 0) && !hasFlag(x - 1, y + 1)) tiles[(y + 1) * 25 + (x - 1)] = Tile(count);
		count = countMines(x + 1, y + 1);
		if((count > 0) && !hasFlag(x + 1, y + 1)) tiles[(y + 1) * 25 + (x + 1)] = Tile(count);
	}
}

void MineSweeper::bang(Engine *engine, int index) {
	if(hasFlag(sweepers[index].x, sweepers[index].y)) return;
	
	addScore(engine, index, -3);
	tiles[sweepers[index].y * 25 + sweepers[index].x] = ExplodedMine;
}

void MineSweeper::flag(Engine *engine, int index) {
	if(flags[sweepers[index].y * 25 + sweepers[index].x] == -1) {
		if(tiles[sweepers[index].y * 25 + sweepers[index].x] == Water) {
			addScore(engine, index, -2);
			recurse(sweepers[index].x, sweepers[index].y);
		} else if(tiles[sweepers[index].y * 25 + sweepers[index].x] == HiddenMine) {
			flags[sweepers[index].y * 25 + sweepers[index].x] = index;
			addScore(engine, index, 2);
		}
	}
}

void MineSweeper::addScore(Engine *engine, int player, int score) {
	engine->players[player].score += score;
	if(engine->players[player].score < 0) engine->players[player].score = 0;
	
	sf::String string;
	string.SetText(val2str(score));
	if(score > 0) string.SetText("+" + string.GetText());
	string.SetFont(engine->getFont("data/fonts/BELIEVER.TTF"));
	string.SetSize(24.f);
	string.SetColor(sf::Color(255, 255, 255, 255));
	string.SetPosition(sweepers[player].target.GetPosition());
	
	if(string.GetPosition().x + string.GetRect().GetWidth() > 800.f) string.Move(sf::Vector2f(800.f - (string.GetPosition().x + string.GetRect().GetWidth()), 0.f));
	if(string.GetPosition().y + string.GetRect().GetHeight() > 600.f) string.Move(sf::Vector2f(0.f, 600.f - (string.GetPosition().y + string.GetRect().GetHeight())));
	
	scoreStrings.push_back(ScoreString(string));
}
